import io
import threading
from threading import Thread
from typing import BinaryIO, TextIO


def io_transfer_by_line(
    in_io: TextIO,
    out_io: TextIO, *,
    buffer_size: int = 1024,
):
    while True:
        piece = in_io.readline(buffer_size)
        if not piece:
            break
        out_io.write(piece)
    out_io.flush()


def io_transfer_by_batch(
    in_io: BinaryIO,
    out_io: BinaryIO, *,
    buffer_size: int = 1024,
):
    while True:
        piece = in_io.read(buffer_size)
        if not piece:
            break
        out_io.write(piece)
    out_io.flush()


def io_transfer(
    in_io: BinaryIO | TextIO,
    out_io: BinaryIO | TextIO, *,
    by_line: bool = False,
    buffer_size: int = 1024,
):
    if by_line:
        if not isinstance(in_io, (TextIO, io.TextIOWrapper)):
            in_io = io.TextIOWrapper(in_io, newline='')
        if not isinstance(out_io, (TextIO, io.TextIOWrapper)):
            out_io = io.TextIOWrapper(out_io, newline='')
        io_transfer_by_line(in_io, out_io, buffer_size=buffer_size)
    else:
        io_transfer_by_batch(in_io, out_io, buffer_size=buffer_size)


def threaded_io_transfer(
    in_io: BinaryIO | TextIO,
    out_io: BinaryIO | TextIO, *,
    by_line: bool = False,
    buffer_size: int = 1024,
) -> Thread:
    thread_func = lambda: io_transfer(in_io, out_io, by_line=by_line, buffer_size=buffer_size)
    thread = threading.Thread(target=thread_func, daemon=True)
    thread.start()
    return thread
